﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using CommonClasses;
using Descriptions_DomainModel.Model;
using CommonClasses.Logger;
using CommonClasses.Utility;
using Eco.ObjectRepresentation;


namespace RunTime
{
    /// <summary>
    /// Класс, осуществляющий загрузку всех нужных управляющих сборок по указанной DM.
    /// </summary>
    /// <remarks>
    /// Для того, чтобы управляющая сборка могла быть загружена с помощью данного класса,
    /// необходимо, чтобы она имела конструктор вида:
    /// .ctor(ILogger logger, NodeGroup nodGroup, IConnection connectionDispatcher, string configPath)
    /// 
    /// nodeGroup - группа узлов, для которой создана данная сборка
    /// connectionDispatcher - диспетчер, управляющий работой соединений (например, USB)
    /// configPath - путь до файла с конфигом для данной сборки
    /// </remarks>
    public class ManagingAssembliesLoader
    {
        #region Private fields

        /// <summary>
        /// Логгер, в который будут валиться сообщения
        /// </summary>
        private ILogger logger = null;
        /// <summary>
        /// Диспетчер, управляющий работой соединений (например, USB).
        /// Нужен для создания экземпляров сборок.
        /// </summary>
        private IConnection dispatcher;

        /// <summary>
        /// Провайдер сервисов ECO
        /// </summary>
        private IEcoServiceProvider serviceProvider;

        #endregion

        /// <summary>
        /// Создать экземпляр класса ManagingAssembliesLoader и проинициалиировать его.
        /// </summary>
        /// <param name="logger">Провайдер сервисов ECO</param>
        /// <param name="logger">Логгер, с которым будет работать данный экземпляр</param>
        /// <param name="dispatcher">
        /// Диспетчер, управляющий работой соединений (например, USB).
        /// Нужен для создания экземпляров сборок.
        /// </param>
        public ManagingAssembliesLoader(IEcoServiceProvider serviceProvider, ILogger logger, IConnection dispatcher)
        {
            this.serviceProvider = serviceProvider;
            this.logger = logger;
            this.dispatcher = dispatcher;
        }

        /// <summary>
        /// Получить список управляющих сборок по указанной DM.
        /// <remarks>
        /// Для того, чтобы управляющая сборка могла быть загружена с помощью данного класса,
        /// необходимо, чтобы она имела конструктор вида:
        /// .ctor(ILogger logger, NodeGroup nodGroup, string configPath)
        /// 
        /// logger - логгер, с которым будет работать сборка
        /// nodeGroup - группа узлов, для которой создана данная сборка
        /// configPath - путь до файла с конфигом для данной сборки
        /// </remarks>
        /// </summary>
        /// <param name="domainModel">Описание оборудования</param>
        /// <returns>Список управляющих сборок</returns>
        public List<IConnection> GetAssemblies(DomainModel domainModel)
        {
            var result = new List<IConnection>();

            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);

            logger.WriteMessage(MessagePriority.Info, "ManagingAssembliesLoader.GetAssemblies called");

            foreach (var nodeGroup in domainModel.NodeGroups)
            {
                if (string.IsNullOrEmpty(nodeGroup.AssemblyFileName))
                    continue;

                IConnection managingAssembly = null;

                logger.WriteMessage(MessagePriority.Info, "Began to loading managing assembly for node group '" + nodeGroup.Name + "'");

                try
                {
                    var assembly = Assembly.LoadFrom(nodeGroup.AssemblyFileName);
                    var assemblyType = assembly.GetType(nodeGroup.AssemblyTypeName, true);
                    var ctor = assemblyType.GetConstructor(new Type[]
                        {serviceProvider.GetType(), nodeGroup.GetType(), dispatcher.GetType()});

                    if (ctor == null)
                        throw new InvalidOperationException("Managing assembly must have constructor coresponding AssemlyLoader request!");

                    managingAssembly = ctor.Invoke(new object[3] {
                            serviceProvider,
                            nodeGroup, 
                            dispatcher            }) as IConnection;

                    logger.WriteMessage(MessagePriority.Info, "Managing assembly instance was created successfully");
                }
                catch(Exception ex)
                {
                    logger.WriteMessage(MessagePriority.Error, 
                        "The error occured during loading managing assembly for node group '" + nodeGroup.Name + "':\n\r" +
                        "FileName: " + nodeGroup.AssemblyFileName + "\n\r" +
                        "TypeName: " + nodeGroup.AssemblyTypeName + "\n\r" +
                        "ConfigFileName: " + nodeGroup.AssemblyConfigFileName);
                    logger.WriteException(ex);
                    throw ex;
                }

                //если не создали, то обрабатываем следующую группу узлов
                if (managingAssembly == null)
                {
                    logger.WriteMessage(MessagePriority.Info, "Managing assembly wasn't loaded succesfully. Processing next node group");
                    continue;
                }
                
                //если всё прошло удачно, то добавляем сборку в вывод
                result.Add(managingAssembly);
            }

            return result;
        }
    }
}
